package org.zjvis.datascience.web.controller;

import cn.hutool.core.util.CharUtil;
import cn.weiguangfu.swagger2.plus.annotation.ApiPlus;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.nimbusds.jose.util.IOUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.zjvis.datascience.common.graph.annotation.CheckNull;
import org.zjvis.datascience.common.graph.annotation.GraphAuth;
import org.zjvis.datascience.common.annotation.ProjectRoleAuth;
import org.zjvis.datascience.common.dto.graph.*;
import org.zjvis.datascience.common.dto.user.UserDTO;
import org.zjvis.datascience.common.enums.ActionEnum;
import org.zjvis.datascience.common.enums.ProjectRoleAuthEnum;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.graph.annotation.GraphFilterAuth;
import org.zjvis.datascience.common.graph.annotation.GraphFilterPipelineAuth;
import org.zjvis.datascience.common.graph.enums.GraphFileFormatEnum;
import org.zjvis.datascience.common.graph.importer.ImporterCSV;
import org.zjvis.datascience.common.graph.util.GraphUtil;
import org.zjvis.datascience.common.model.ApiResult;
import org.zjvis.datascience.common.model.ApiResultCode;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.common.vo.graph.*;
import org.zjvis.datascience.service.MinioService;
import org.zjvis.datascience.service.WidgetService;
import org.zjvis.datascience.service.graph.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @description 图分析接口 Controller
 * @date 2021-10-28
 */
@ApiPlus(value = true)
@RequestMapping("/graphAnalysis")
@RestController
@Api(tags = "graphAnalysis", description = "图分析接口")
@Validated
public class GraphAnalysisController {
    @Autowired
    private GraphAnalysisService graphAnalysisService;

    @Autowired
    private GraphService graphService;

    @Autowired
    private MinioService minioService;

    @Autowired
    private GraphFilterService graphFilterService;

    @Autowired
    private GraphFilterPipelineService graphFilterPipelineService;

    @Autowired
    private GraphFilterPipelineInstanceService graphFilterPipelineInstanceService;

    @Autowired
    private GraphActionService graphActionService;

    @Autowired
    private WidgetService widgetService;

    public static String GRAPH_FILE_DIR = "graph-file";

    @PostMapping("/uploadFile")
    @ApiOperation(value = "图文件上传", notes = "图文件上传")
    public ApiResult<String> uploadFile(@Valid @NotNull @RequestParam ("file") MultipartFile file) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        try {
            graphAnalysisService.checkGraphFile(file);
        } catch (DataScienceException e) {
            throw e;
        } catch (Exception e) {
            throw new DataScienceException(BaseErrorCode.GRAPH_FILE_PARSE_ERROR);
        }
        String userName = user.getName();
        String fileName = file.getOriginalFilename();
        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");
        int i = 1;
        String ext = FilenameUtils.getExtension(fileName);
        String baseName = FilenameUtils.removeExtension(fileName);
        while (objectNames.contains(fileName)) {
            fileName = baseName + CharUtil.UNDERLINE + i++ + CharUtil.DOT + ext;
        }
        minioService.putObject(GRAPH_FILE_DIR, userName + "/" + fileName, file.getInputStream());
        return ApiResult.valueOf(fileName);
    }

    @PostMapping("/previewFile")
    @ApiOperation(value = "图文件预览", notes = "图文件预览")
    public ApiResult<JSONObject> previewFile(@Valid @NotNull @RequestParam("file") MultipartFile file) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String fileName = file.getOriginalFilename().toLowerCase();
        if (!fileName.endsWith(".csv")&&!fileName.endsWith(".json")&&!fileName.endsWith(".gml")) {
            return ApiResult.valueOf(ApiResultCode.GRAPH_LOAD_FORMAT_ERROR);
        }
        JSONObject ret = new JSONObject();
        String format;
        Object content;
        InputStream in = file.getInputStream();
        if (fileName.endsWith(".json")) {
            format = GraphFileFormatEnum.JSON.getDesc();
            content = IOUtils.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else if (fileName.endsWith(".csv")) {
            format = GraphFileFormatEnum.CSV.getDesc();
            content = ImporterCSV.parseCSV2JSON(in, 20);
        } else if (fileName.endsWith(".gml")) {
            format = GraphFileFormatEnum.GML.getDesc();
            content = IOUtils.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else {
            return ApiResult.valueOf(ApiResultCode.GRAPH_LOAD_FORMAT_ERROR);
        }
        ret.put("format", format);
        ret.put("content", content);
        return ApiResult.valueOf(ret);
    }

    @PostMapping("/listFiles")
    @ApiOperation(value = "图文件列表", notes = "图文件列表")
    public ApiResult<List<String>> listFiles(HttpServletRequest request) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String userName = user.getName();
        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");
        return ApiResult.valueOf(objectNames);
    }

    @PostMapping("/updateFileName")
    @ApiOperation(value = "图文件重命名", notes = "图文件重命名")
    public ApiResult<List<String>> updateFileName(HttpServletRequest request,
                                                  @RequestBody
                                                  @CheckNull(field = "fileName")
                                                  @CheckNull(field = "update")
                                                          JSONObject params) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        String userName = user.getName();
        String fileName = params.getString("fileName");
        String updateName = params.getString("update");

        if (!FilenameUtils.getExtension(fileName).equals(FilenameUtils.getExtension(updateName))) {
            throw new DataScienceException(BaseErrorCode.DATASET_GRAPH_FILE_EXT_CHANGE);
        }


        if (StringUtils.isEmpty(fileName)) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        if (fileName.equals(updateName)) {
            return ApiResult.valueOf(ApiResultCode.SUCCESS);
        }

        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");

        if (!objectNames.contains(fileName)) {
            return ApiResult.valueOf(ApiResultCode.DATA_NULL);
        }

        if (objectNames.contains(updateName)) {
            throw new DataScienceException(BaseErrorCode.DATASET_NAME_DUPLICATE_ERROR);
        }

        InputStream in = minioService.getObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        minioService.putObject(GRAPH_FILE_DIR, userName + "/" + updateName, in);
        minioService.deleteObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping("/deleteFile")
    @ApiOperation(value = "图文件删除", notes = "图文件删除")
    public ApiResult<Void> deleteFile(HttpServletRequest request, @RequestBody @CheckNull(field = "fileName") JSONObject params) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String userName = user.getName();
        String fileName = params.getString("fileName");
        if (StringUtils.isEmpty(fileName)) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        minioService.deleteObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping("/queryFileByName")
    @ApiOperation(value = "图文件预览", notes = "图文件预览")
    public ApiResult<JSONObject> queryFileByName(HttpServletRequest request, @RequestBody @CheckNull(field = "fileName") JSONObject params) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String userName = user.getName();
        String fileName = params.getString("fileName");
        if (StringUtils.isEmpty(fileName)) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }

        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");

        if (!objectNames.contains(fileName)) {
            return ApiResult.valueOf(ApiResultCode.DATA_NULL);
        }

        JSONObject ret = new JSONObject();
        String format;
        Object content;
        InputStream in = minioService.getObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        if (fileName.endsWith(".json")) {
            format = GraphFileFormatEnum.JSON.getDesc();
            content = IOUtils.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else if (fileName.endsWith(".csv")) {
            format = GraphFileFormatEnum.CSV.getDesc();
            content = ImporterCSV.parseCSV2JSON(in, 20);
        } else if (fileName.endsWith(".gml")) {
            format = GraphFileFormatEnum.GML.getDesc();
            content = IOUtils.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else {
            return ApiResult.valueOf(ApiResultCode.GRAPH_LOAD_FORMAT_ERROR);
        }
        ret.put("format", format);
        ret.put("content", content);
        return ApiResult.valueOf(ret);
    }



    @PostMapping(value = "/queryGraphByProjectId")
    @ResponseBody
    @ApiOperation(value = "queryGraphByProjectId", notes = "查询图分析面板列表")
    @Transactional
    public ApiResult<List<GraphVO>> queryGraphByProjectId(HttpServletRequest request,
                                                 @RequestBody
                                                 @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                         JSONObject params) {
        List<GraphVO> tabs = new ArrayList<>();
        Long projectId = params.getLong("projectId");
        List<GraphDTO> graphs = graphService.queryByProjectId(projectId);
        if (graphs.size() == 0) {
            GraphDTO graph = graphAnalysisService.addGraphByProject(projectId);
            graphs.add(graph);
        }
        for (GraphDTO graph: graphs) {
            tabs.add(graph.tab());
        }
        return ApiResult.valueOf(tabs);
    }

    @PostMapping(value = "/addGraph")
    @ResponseBody
    @ApiOperation(value = "addGraph", notes = "增加新面板")
    @Transactional
    public ApiResult<GraphVO> addGraph(HttpServletRequest request,
                                       @RequestBody
                                       @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                               JSONObject params) {
        Long projectId = params.getLong("projectId");
        GraphDTO graph = graphAnalysisService.addGraphByProject(projectId);
        return ApiResult.valueOf(graph.view());
    }

    @PostMapping(value = "/deleteGraphById")
    @ResponseBody
    @ApiOperation(value = "deleteGraphById", notes = "删除面板")
    @Transactional
    public ApiResult<Void> deleteGraphById(HttpServletRequest request,
                                       @RequestBody
                                       @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                       @GraphAuth
                                               JSONObject params) {
        Long graphId = params.getLong("graphId");
        graphFilterPipelineInstanceService.deleteByGraphId(graphId);
        graphFilterPipelineService.deleteByGraphId(graphId);
        graphFilterService.deleteByGraphId(graphId);
        widgetService.deleteGraphAnalysisById(graphId);
        graphService.deleteById(graphId);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/queryById")
    @ResponseBody
    @ApiOperation(value = "queryById", notes = "查询图分析视图数据")
    @Transactional
    public ApiResult<GraphVO> queryById(HttpServletRequest request,
                                        @RequestBody
                                        @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                        @GraphAuth
                                                JSONObject params) {
        Long graphId = params.getLong("graphId");
        GraphDTO graphDTO = graphService.queryById(graphId);
        graphAnalysisService.queryIdMap(graphDTO);
        GraphFilterPipelineInstanceDTO filterInstance = graphAnalysisService.isApplyFilter(graphDTO);
        if (filterInstance != null) {
            graphAnalysisService.applyFilter(graphDTO, filterInstance);
        }
        GraphVO graphVO = graphDTO.view();
        return ApiResult.valueOf(graphVO);
    }

    @PostMapping(value = "/renameTab")
    @ResponseBody
    @ApiOperation(value = "renameTab", notes = "重命名面板")
    @Transactional
    public ApiResult<Void> renameTab(HttpServletRequest request,
                                        @RequestBody
                                        @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                        @GraphAuth
                                        @CheckNull(field = "name")
                                                JSONObject params) {
        GraphDTO graphDTO = graphService.queryById(params.getLong("graphId"));
        graphDTO.setName(params.getString("name"));
        graphService.update(graphDTO);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/loadData")
    @ResponseBody
    @ApiOperation(value = "loadData", notes = "加载数据")
    @Transactional
    public ApiResult<Long> loadData(HttpServletRequest request,
                                     @RequestBody
                                     @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                     @GraphAuth
                                     @CheckNull(field = "fileName")
                                             JSONObject params) {
        Long graphId = params.getLong("graphId");
        String fileName = params.getString("fileName");
        GraphFileFormatEnum format;
        if (fileName.endsWith(".json")) {
            format = GraphFileFormatEnum.JSON;
        } else if (fileName.endsWith(".csv")) {
            format = GraphFileFormatEnum.CSV;
        } else if (fileName.endsWith(".gml")) {
            format = GraphFileFormatEnum.GML;
        } else {
            return ApiResult.valueOf(ApiResultCode.GRAPH_LOAD_FORMAT_ERROR);
        }
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String userName = user.getName();
        return ApiResult.valueOf(graphAnalysisService.loadData(graphId,
                GRAPH_FILE_DIR,
                userName + "/" + fileName, format));
    }

    @PostMapping(value = "/queryLoadStatus")
    @ResponseBody
    @ApiOperation(value = "queryLoadStatus", notes = "查询加载状态")
    @Transactional
    public ApiResult<JSONObject> queryLoadStatus(HttpServletRequest request,
                                    @RequestBody
                                    @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                    @GraphAuth
                                    @CheckNull(field = "id")
                                            JSONObject params) {
        Long id;
        try {
            id = params.getLong("id");
        } catch (Exception e) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        return ApiResult.valueOf(graphAnalysisService.queryLoadStatus(id));
    }

    @PostMapping(value = "/batchDeleteNodesAndLinks")
    @ResponseBody
    @ApiOperation(value = "batchDeleteNodesAndLinks", notes = "批量删除节点和边")
    @Transactional
    public ApiResult<Void> batchDeleteNodesAndLinks(HttpServletRequest request,
                                                    @RequestBody
                                                    @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                    @GraphAuth
                                                            JSONObject params) {
        Long gid = params.getLong("graphId");
        List<String> nids = new ArrayList<>();
        List<String> lids = new ArrayList<>();
        if (params.containsKey("nodeIds")) {
            nids = GraphUtil.parseJSONArray2List(params, "nodeIds", String.class);
        }
        if (params.containsKey("linkIds")) {
            lids = GraphUtil.parseJSONArray2List(params, "linkIds", String.class);
        }
        GraphVO ctx1 = new GraphVO();
        graphAnalysisService.batchDeleteNodesAndLinks(gid, nids, lids, ctx1);
        graphActionService.addActionForGraph(ActionEnum.DELETE, ctx1, null);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/batchUpdate")
    @ResponseBody
    @ApiOperation(value = "batchUpdate", notes = "批量更新")
    @Transactional
    public ApiResult<Void> batchUpdate(HttpServletRequest request,
                                       @RequestBody
                                       @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                       @GraphAuth
                                               JSONObject params) {
        Long gid = params.getLong("graphId");
        List<Float> size = GraphUtil.parseJSONArray2List(params, "size", Float.class);
        List<NodeVO> nodes = GraphUtil.parseJSONArray2List(params, "nodes", NodeVO.class);
        List<LinkVO> links = GraphUtil.parseJSONArray2List(params, "links", LinkVO.class);

        Boolean needUndo = params.getBoolean("needUndo");
        JSONObject actionContext = params.getJSONObject("actionContext");
        JSONObject context = new JSONObject();
        if (needUndo == null || needUndo) {
            GraphVO ctx1 = new GraphVO();
            GraphVO ctx2 = new GraphVO();
            graphService.batchUpdate(gid, nodes, links, size, ctx1, ctx2, actionContext);
            graphActionService.addActionForGraph(ActionEnum.UPDATE, ctx1, ctx2, null, context);
        } else {
            graphService.batchUpdate(gid, nodes, links, size);
        }
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/addFilterPipeline")
    @ResponseBody
    @ApiOperation(value = "addFilterPipeline", notes = "添加过滤流程图")
    @Transactional
    public ApiResult<GraphFilterPipelineVO> addFilterPipeline(HttpServletRequest request,
                                          @RequestBody
                                          @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                          @GraphAuth
                                                  JSONObject params) {
        GraphFilterPipelineVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterPipelineVO.class);
        if (vo.getName() == null) {
            vo.setName("过滤1");
        }
        vo.setUserId(JwtUtil.getCurrentUserId());
        GraphFilterPipelineDTO dto = vo.dto();
        graphFilterPipelineService.save(dto);
        return ApiResult.valueOf(dto.view());
    }

    @PostMapping(value = "/deleteFilterPipeline")
    @ResponseBody
    @ApiOperation(value = "deleteFilterPipeline", notes = "删除过滤流程图")
    @Transactional
    public ApiResult<Void> deleteFilterPipeline(HttpServletRequest request,
                                                @RequestBody
                                                @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                @GraphAuth
                                                @GraphFilterPipelineAuth
                                                        JSONObject params) {
        GraphFilterPipelineVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterPipelineVO.class);
        graphFilterPipelineInstanceService.deleteByPipelineId(vo.getId());
        graphFilterService.deleteByGraphFilterPipelineId(vo.getId());
        graphFilterPipelineService.deleteById(vo.getId());
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/queryFilterPipelineByGraphId")
    @ResponseBody
    @ApiOperation(value = "queryFilterPipelineByGraphId", notes = "查询所有过滤流程图")
    @Transactional
    public ApiResult<List<GraphFilterPipelineVO>> queryFilterPipelineByGraphId(HttpServletRequest request,
                                                              @RequestBody
                                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                              @GraphAuth
                                                                      JSONObject params) {
        GraphFilterPipelineVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterPipelineVO.class);
        Long graphId = vo.getGraphId();
        List<GraphFilterPipelineVO> ret = new ArrayList<>();
        List<GraphFilterPipelineDTO> dtos = graphFilterPipelineService.queryByGraphId(graphId);
        if (dtos.size() == 0) {
            GraphFilterPipelineVO newVO = new GraphFilterPipelineVO();
            newVO.setGraphId(vo.getGraphId());
            newVO.setProjectId(vo.getProjectId());
            newVO.setName("过滤1");
            newVO.setUserId(JwtUtil.getCurrentUserId());
            GraphFilterPipelineDTO dto = newVO.dto();
            graphFilterPipelineService.save(dto);
            ret.add(dto.view());
            return ApiResult.valueOf(ret);
        }
        for (GraphFilterPipelineDTO dto: dtos) {
            ret.add(dto.view());
        }
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/updateFilterPipeline")
    @ResponseBody
    @ApiOperation(value = "updateFilterPipeline", notes = "更新过滤流程图信息")
    @Transactional
    public ApiResult<Void> updateFilterPipeline(HttpServletRequest request,
                                             @RequestBody
                                             @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                             @GraphAuth
                                             @GraphFilterPipelineAuth
                                                     JSONObject params) {
        GraphFilterPipelineVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterPipelineVO.class);
        GraphFilterPipelineDTO dto = vo.dto();
        graphFilterPipelineService.update(dto);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }


    @PostMapping(value = "/addFilter")
    @ResponseBody
    @ApiOperation(value = "addFilter", notes = "添加过滤器")
    @Transactional
    public ApiResult<GraphFilterVO> addFilter(HttpServletRequest request,
                                                              @RequestBody
                                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                              @GraphAuth
                                                              @GraphFilterPipelineAuth(field = "graphFilterPipelineId")
                                                                      JSONObject params) {
        GraphFilterVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterVO.class);
        if (vo == null
                || vo.getType() == null
                || vo.getSubType() == null) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        vo.setUserId(JwtUtil.getCurrentUserId());
        if (vo.getData() == null) {
            vo.setData(new JSONObject());
        }
        GraphFilterDTO dto = graphFilterService.loadAndSave(vo);
        graphService.closeTransactions();
        return ApiResult.valueOf(dto.view());
    }

    @PostMapping(value = "/deleteFilter")
    @ResponseBody
    @ApiOperation(value = "deleteFilter", notes = "删除过滤器")
    @Transactional
    public ApiResult<Void> deleteFilter(HttpServletRequest request,
                                              @RequestBody
                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                              @GraphAuth
                                              @GraphFilterAuth
                                                      JSONObject params) {
        GraphFilterVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterVO.class);
        graphFilterService.deleteFilter(vo.getId());
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/queryFilterByFilterPipelineId")
    @ResponseBody
    @ApiOperation(value = "queryFilterByFilterPipelineId", notes = "查询给定流程下的过滤器")
    @Transactional
    public ApiResult<List<GraphFilterVO>> queryFilterByFilterPipelineId(HttpServletRequest request,
                                              @RequestBody
                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                              @GraphAuth
                                              @GraphFilterPipelineAuth(field = "graphFilterPipelineId")
                                                      JSONObject params) {
        GraphFilterVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterVO.class);
        List<GraphFilterDTO> dtos = graphFilterService.queryByGraphFilterPipelineId(vo.getGraphFilterPipelineId());
        List<GraphFilterVO> ret = new ArrayList<>();
        for (GraphFilterDTO dto: dtos) {
            ret.add(dto.view());
        }
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/updateFilter")
    @ResponseBody
    @ApiOperation(value = "updateFilter", notes = "更新过滤器")
    @Transactional
    public ApiResult<GraphFilterVO> updateFilter(HttpServletRequest request,
                                        @RequestBody
                                        @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                        @GraphAuth
                                        @GraphFilterPipelineAuth(field = "graphFilterPipelineId")
                                        @GraphFilterAuth
                                        @CheckNull(field = "data")
                                                JSONObject params) {
        GraphFilterVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterVO.class);
        if (vo == null
                || vo.getType() == null
                || vo.getSubType() == null) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        graphFilterService.updateFilter(vo);
        return ApiResult.valueOf(vo);
    }

    @PostMapping(value = "/executeFilterPipeline")
    @ResponseBody
    @ApiOperation(value = "executeFilterPipeline", notes = "执行过滤器流程")
    @Transactional
    public ApiResult<Void> executeFilterPipeline(HttpServletRequest request,
                                        @RequestBody
                                        @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                        @GraphAuth
                                        @GraphFilterPipelineAuth
                                                JSONObject params) {
        GraphFilterPipelineVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterPipelineVO.class);
        GraphDTO graph = graphService.queryById(vo.getGraphId());
        JSONObject graphObj = JSONObject.parseObject(graph.getDataJson());

        //操作前
        GraphVO ctx1 = new GraphVO();
        GraphVO ctx2 = new GraphVO();
        graph.initActionView(ctx1);
        graph.initActionView(ctx2);
        GraphFilterPipelineInstanceDTO filterInstance1 = graphAnalysisService.isApplyFilter(graph);
        ctx1.setFilterInstanceId(filterInstance1 == null ? null : filterInstance1.getId());


        List<CategoryVO> categories = graphObj.getJSONArray("categories").toJavaList(CategoryVO.class);
        List<String> activateMetric = graphAnalysisService.queryActivateMetricAttr(graph);
        Long instanceId = graphFilterService.executeFilterPipeline(vo.getGraphId(), vo.getId(), categories, activateMetric);
        graphObj.put("filterPipelineInstance", instanceId);
        graph.setDataJson(graphObj.toJSONString());
        graphService.update(graph);
        graphService.closeTransactions();

        //操作后
        ctx2.setFilterInstanceId(instanceId);

        graphActionService.addActionForGraph(ActionEnum.GRAPH_FILTER, ctx1, ctx2, null);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/resetFilterPipeline")
    @ResponseBody
    @ApiOperation(value = "resetFilterPipeline", notes = "重置过滤器流程")
    @Transactional
    public ApiResult<Void> resetFilterPipeline(HttpServletRequest request,
                                                 @RequestBody
                                                 @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                 @GraphAuth
                                                         JSONObject params) {
        GraphDTO graph = graphService.queryById(params.getLong("graphId"));
        JSONObject graphObj = JSONObject.parseObject(graph.getDataJson());

        //操作前
        GraphVO ctx1 = new GraphVO();
        GraphVO ctx2 = new GraphVO();
        graph.initActionView(ctx1);
        graph.initActionView(ctx2);
        GraphFilterPipelineInstanceDTO filterInstance1 = graphAnalysisService.isApplyFilter(graph);
        ctx1.setFilterInstanceId(filterInstance1 == null ? null : filterInstance1.getId());

        Long instanceId = null;

        graphObj.put("filterPipelineInstance", instanceId);
        graph.setDataJson(graphObj.toJSONString());
        graphService.update(graph);

        //操作后
        ctx2.setFilterInstanceId(instanceId);
        graphActionService.addActionForGraph(ActionEnum.GRAPH_FILTER, ctx1, ctx2, null);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/queryFilterAvailableAttrs")
    @ResponseBody
    @ApiOperation(value = "queryFilterAvailableAttrs", notes = "查询当前过滤器可用属性列表")
    @Transactional
    public ApiResult<JSONObject> queryFilterAvailableAttrs(HttpServletRequest request,
                                                 @RequestBody
                                                 @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                 @GraphAuth
                                                 @GraphFilterPipelineAuth(field = "graphFilterPipelineId")
                                                         JSONObject params) {
        GraphFilterVO vo = JSONObject.parseObject(params.toJSONString(), GraphFilterVO.class);
        GraphDTO graph = graphService.queryById(vo.getGraphId());
        JSONObject graphObj = JSONObject.parseObject(graph.getDataJson());
        List<CategoryVO> categories = graphObj.getJSONArray("categories").toJavaList(CategoryVO.class);
        List<String> activateMetric = graphAnalysisService.queryActivateMetricAttr(graph);
        JSONObject ret = graphFilterService.queryFilterAvailableAttrs(categories, vo.getParentId(), activateMetric);
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryAvailableAttrs")
    @ResponseBody
    @ApiOperation(value = "queryAvailableAttrs", notes = "通用的可用属性列表")
    @Transactional
    public ApiResult<JSONObject> queryAvailableAttrs(HttpServletRequest request,
                                                           @RequestBody
                                                           @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                           @GraphAuth
                                                           @CheckNull(field = "categoryIds")
                                                                   JSONObject params) {
        Long graphId = params.getLong("graphId");
        List<String> categoryIds = GraphUtil.parseJSONArray2List(params, "categoryIds", String.class);
        GraphDTO graph = graphService.queryById(graphId);
        JSONObject graphObj = JSONObject.parseObject(graph.getDataJson());
        List<CategoryVO> categories = graphObj.getJSONArray("categories").toJavaList(CategoryVO.class);
        List<CategoryVO> filterCategories = categories.stream().filter(c->categoryIds.contains(c.getId())).collect(Collectors.toList());
        List<String> activateMetric = graphAnalysisService.queryActivateMetricAttr(graph);
        JSONObject ret = graphAnalysisService.queryAvailableAttrs(filterCategories, activateMetric);
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryAttrStyle")
    @ResponseBody
    @ApiOperation(value = "queryAttrStyle", notes = "查询属性分组着色及排序")
    @Transactional
    public ApiResult<JSONArray> queryAttrStyle(HttpServletRequest request,
                                                               @RequestBody
                                                               @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                               @GraphAuth
                                                               @CheckNull(field = "attr")
                                                               @CheckNull(field = "categoryIds")
                                                               @CheckNull(field = "mode")
                                                                       JSONObject params) {
        Long gid = params.getLong("graphId");
        String attr = params.getString("attr");
        String mode = params.getString("mode");
        List<String> categoryIds = params.getJSONArray("categoryIds").toJavaList(String.class);
        JSONArray ret = new JSONArray();
        GraphDTO graphDTO = graphService.queryById(gid);
        if (mode.equals("group")) {
            ret = graphAnalysisService.queryAttrGroup(graphDTO, categoryIds, attr);
        } else if (mode.equals("sort")) {
            ret = graphAnalysisService.queryAttrSort(graphDTO, categoryIds, attr);
        }
        graphService.closeTransactions();
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryMetrics")
    @ResponseBody
    @ApiOperation(value = "queryMetrics", notes = "查询数据统计指标")
    @Transactional
    public ApiResult<JSONObject> queryMetrics(HttpServletRequest request,
                                                               @RequestBody
                                                               @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                               @GraphAuth
                                                                       JSONObject params) {
        Long gid = params.getLong("graphId");
        JSONObject ret = graphAnalysisService.queryMetrics(gid);
        graphService.closeTransactions();
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/executeMetrics")
    @ResponseBody
    @ApiOperation(value = "executeMetrics", notes = "执行数据统计指标")
    @Transactional
    public ApiResult<JSONObject> executeMetrics(HttpServletRequest request,
                                              @RequestBody
                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                              @GraphAuth
                                              @CheckNull(field = "type")
                                                      JSONObject params) {
        Long gid = params.getLong("graphId");
        String type = params.getString("type");
        JSONObject ret = graphAnalysisService.executeMetrics(gid, type);
        graphService.closeTransactions();
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryPageRank")
    @ResponseBody
    @ApiOperation(value = "queryPageRank", notes = "运行pagerank算法")
    @Transactional
    public ApiResult<JSONObject> queryPageRank(HttpServletRequest request,
                                              @RequestBody
                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                              @GraphAuth
                                                      JSONObject params) {
        Long gid = params.getLong("graphId");
        JSONObject ret = graphAnalysisService.queryPageRank(gid);
        graphService.closeTransactions();
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryCluster")
    @ResponseBody
    @ApiOperation(value = "queryCluster", notes = "运行社区发现算法")
    @Transactional
    public ApiResult<JSONObject> queryCluster(HttpServletRequest request,
                                               @RequestBody
                                               @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                               @GraphAuth
                                                       JSONObject params) {
        Long gid = params.getLong("graphId");
        JSONObject ret = graphAnalysisService.queryCluster(gid);
        graphService.closeTransactions();
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/activateMetric")
    @ResponseBody
    @ApiOperation(value = "activateMetric", notes = "激活/取消统计属性")
    @Transactional
    public ApiResult<Void> activateMetric(HttpServletRequest request,
                                              @RequestBody
                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                              @GraphAuth
                                              @CheckNull(field = "metricTag")
                                              @CheckNull(field = "activate")
                                                      JSONObject params) {
        Long gid = params.getLong("graphId");
        String metricTag = params.getString("metricTag");
        Boolean activate;
        try {
            activate = params.getBoolean("activate");
        } catch (Exception e) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        Boolean ret = graphAnalysisService.activateMetric(gid, metricTag, activate);
        if (ret) {
            return ApiResult.valueOf(ApiResultCode.SUCCESS);
        } else {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
    }

    @PostMapping(value = "/queryDataDetail")
    @ResponseBody
    @ApiOperation(value = "queryDataDetail", notes = " 数据详情")
    @Transactional
    public ApiResult<JSONObject> queryDataDetail(HttpServletRequest request,
                                              @RequestBody
                                              @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                              @GraphAuth
                                              @CheckNull(field = "curPage")
                                              @CheckNull(field = "pageSize")
                                              @CheckNull(field = "type")
                                                      JSONObject params) {
        String type = params.getString("type");
        Long gid = params.getLong("graphId");
        Integer curPage;
        Integer pageSize;
        try {
            curPage = params.getInteger("curPage");
            pageSize = params.getInteger("pageSize");
        } catch (Exception e) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }

        JSONObject ret = new JSONObject();
        if (type.equals("node")) {
            if (!params.containsKey("categoryId")) {
                return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
            }
            String cid = params.getString("categoryId");
            ret = graphAnalysisService.queryNodeDetail(gid, cid, curPage, pageSize);
        }
        else if( type.equals("link")) {
            if (!params.containsKey("edgeId")) {
                return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
            }
            String eid = params.getString("edgeId");
            ret = graphAnalysisService.queryLinkDetail(gid, eid, curPage, pageSize);
        }
        graphService.closeTransactions();
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/search")
    @ResponseBody
    @ApiOperation(value = "search", notes = "search")
    @Transactional
    public ApiResult<JSONObject> search(HttpServletRequest request,
                                        @RequestBody
                                        @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                        @GraphAuth
                                        @CheckNull(field = "categoryIds")
                                        @CheckNull(field = "predicate")
                                                JSONObject params) {
        Long gid = params.getLong("graphId");
        List<String> categoryIds = GraphUtil.parseJSONArray2List(params, "categoryIds", String.class);
        String predicate = params.getString("predicate");
        JSONObject ret = graphAnalysisService.search(gid, categoryIds, 2, predicate);
        graphService.closeTransactions();
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryPath")
    @ResponseBody
    @ApiOperation(value = "queryPath", notes = "queryPath")
    @Transactional
    public ApiResult<List<List<String>>> queryPath(HttpServletRequest request,
                                                   @RequestBody
                                                   @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                   @GraphAuth
                                                   @CheckNull(field = "srcId")
                                                   @CheckNull(field = "tarId")
                                                   @CheckNull(field = "type")
                                                           JSONObject params) {
        Long gid = params.getLong("graphId");
        String srcId = params.getString("srcId");
        String tarId = params.getString("tarId");
        String type = params.getString("type");
        Integer maxStep;
        try {
            maxStep = params.getInteger("maxStep");
        } catch (Exception e) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        if (maxStep == null) {
            maxStep = 5;
        }
        List<List<String>> results = graphAnalysisService.queryPath(gid, srcId, tarId, type, maxStep);
        graphService.closeTransactions();
        return ApiResult.valueOf(results);
    }

    @PostMapping(value = "/saveWidget")
    @ResponseBody
    @ApiOperation(value = "saveWidget", notes = "saveWidget")
    @Transactional
    public ApiResult<Long> saveWidget(HttpServletRequest request,
                                      @RequestBody
                                      @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                      @GraphAuth
                                              JSONObject params) {
        Long gid = params.getLong("graphId");
        Long WidgetId = graphService.saveWidget(gid, "graph_analysis", null);
        return ApiResult.valueOf(WidgetId);
    }

    @RequestMapping(value = "/download")
    @ApiOperation(value = "download", notes = "下载数据")
    public void download(HttpServletRequest request, HttpServletResponse response,
                         @RequestParam("graphId") @GraphAuth(field =  StringUtils.EMPTY) String graphId,
                         @RequestParam(value = "format", required = false) String format) {
        if (format == null) {
            format = GraphFileFormatEnum.JSON.getDesc();
        }
        GraphDTO graph = graphService.queryById(Long.parseLong(graphId));
        GraphFilterPipelineInstanceDTO filterInstance = graphAnalysisService.isApplyFilter(graph);
        if (filterInstance != null) {
            graphAnalysisService.applyFilter(graph, filterInstance);
        }
        graphService.downloadData(response, graph, format, false);
    }

    @PostMapping(value = "/undo")
    @ResponseBody
    @ApiOperation(value = "undo", notes = "undo")
    @Transactional
    public ApiResult<JSONObject> undo(HttpServletRequest request,
                                      @RequestBody
                                      @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                      @GraphAuth
                                              JSONObject params) {
        Long gid = params.getLong("graphId");
        GraphActionDTO graphActionDTO = graphActionService.undo(gid);
        graphService.closeTransactions();
        if (graphActionDTO == null) {
            return ApiResult.valueOf(ApiResultCode.UNDO_FAILED, null, "撤销失败， 撤销栈空");
        } else {
            return ApiResult.valueOf(graphActionService.actionElementAna(graphActionDTO, true));
        }
    }

    @PostMapping(value = "/redo")
    @ResponseBody
    @ApiOperation(value = "redo", notes = "redo")
    @Transactional
    public ApiResult<JSONObject> redo(HttpServletRequest request,
                                      @RequestBody
                                      @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                      @GraphAuth
                                              JSONObject params) {
        Long gid = params.getLong("graphId");
        GraphActionDTO graphActionDTO = graphActionService.redo(gid);
        graphService.closeTransactions();
        if (graphActionDTO == null) {
            return ApiResult.valueOf(ApiResultCode.REDO_FAILED, null, "重做失败, 重做栈空");
        } else {
            return ApiResult.valueOf(graphActionService.actionElementAna(graphActionDTO, false));
        }
    }

    @PostMapping(value = "/queryAvailableCategory")
    @ResponseBody
    @ApiOperation(value = "queryAvailableCategory", notes = "查询可用节点类和边类")
    @Transactional
    public ApiResult<JSONObject> queryAvailableCategory(HttpServletRequest request,
                                                        @RequestBody
                                                        @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                        @GraphAuth
                                                                JSONObject params) {
        Long graphId = params.getLong("graphId");
        GraphDTO graphDTO = graphService.queryById(graphId);
        GraphFilterPipelineInstanceDTO filterInstance = graphAnalysisService.isApplyFilter(graphDTO);
        if (filterInstance != null) {
            graphAnalysisService.applyFilter(graphDTO, filterInstance);
        }
        GraphVO graphVO = graphDTO.view();
        JSONObject ret = new JSONObject();
        ret.put("categories", graphVO.getCategories());
        ret.put("edges", graphVO.getEdges());
        return ApiResult.valueOf(ret);
    }

    @PostMapping(value = "/queryHeatMap")
    @ResponseBody
    @ApiOperation(value = "queryHeatMap", notes = "热图查询")
    @Transactional
    public ApiResult<Map<String, Double>> queryHeatMap(HttpServletRequest request,
                                                       @RequestBody
                                                       @ProjectRoleAuth(role = ProjectRoleAuthEnum.CREATOR)
                                                       @GraphAuth
                                                       @CheckNull(field = "source")
                                                               JSONObject params) {
        Long graphId = params.getLong("graphId");
        String source = params.getString("source");
        Map<String, Double> distanceMap = graphAnalysisService.queryHeatMap(graphId, source);
        graphService.closeTransactions();
        return ApiResult.valueOf(distanceMap);
    }
}
